
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Threading;
using Swaf;
using Swaf.Container;

namespace Swaf.Versioning
{
	/// <summary>
	/// This versioining dictionary uses a hash table as its internal storage.  We use a ReaderWriter lock to synchronize
	/// read and write access to make it safe for use in multi-threaded access.  All methods that pertain to reading or writing
	/// to either the Source or Cache dictionaries are overridden to provide locking and thread-safety.
	/// </summary>
	[Serializable]
	public class HashVersioningDictionary : BaseVersioningDictionary, IMap
	{
		//We will use ReaderWrite locks to synchronize access into the HashTables
		static ReaderWriterLock cacheLock = new ReaderWriterLock();
		static ReaderWriterLock sourceLock = new ReaderWriterLock();
		const int TIMEOUT=100;
		
		/// <summary>
		/// Overridden Constructor to implement the source and cache dictionaries as Hashtables.
		/// </summary>
		public HashVersioningDictionary() : base()
		{
			m_Source = new FlexiMap();
			m_Cache = new FlexiMap();
		}
		#region BaseVersioningDictionary Members

		/// <summary>
		/// Overridden BaseVersionDictionary readFromCache method.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		protected override object readFromCache(string key)
		{
			object retVal=null;
			cacheLock.AcquireReaderLock(TIMEOUT);
			try
			{
				retVal = m_Cache[key];
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				cacheLock.ReleaseReaderLock();
			}
			return retVal;
		}
		/// <summary>
		/// Overridden BaseVersionDictionary writeToCache method.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		protected override void writeToCache(string key, object value, object extraInfo)
		{
			cacheLock.AcquireWriterLock(TIMEOUT);
			try
			{
				if ((m_expiretype == null && extraInfo == null) || !(m_Cache is INameAddressSupport))
				{
					m_Cache[key]=value;
				}
				else
				{
					((INameAddressSupport) m_Cache).put(key,value, (extraInfo == null) ? m_expiretype : extraInfo);
				}
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				cacheLock.ReleaseWriterLock();
			}
		}
		
		/// <summary>
		/// Overridden BaseVersionDictionary  readFromSource method.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		protected override object readFromSource(string key)
		{
			object retVal=null;
			sourceLock.AcquireReaderLock(TIMEOUT);
			try
			{
				retVal=m_Source[key];
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{

				sourceLock.ReleaseReaderLock();
			}
			return retVal;
		}
		
		/// <summary>
		/// Overridden BaseVersionDictionary  writeToSource method.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		protected override void writeToSource(string key, object value, object extraInfo)
		{
			sourceLock.AcquireWriterLock(TIMEOUT);
			try
			{
				if ((m_expiretype == null && extraInfo == null) || !(m_Source is INameAddressSupport))
				{
					m_Source.Add((IComparable) key, value);
				}
				else
				{
					((INameAddressSupport) m_Source).put(key,value, (extraInfo == null) ? m_expiretype : extraInfo);
				}
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				sourceLock.ReleaseWriterLock();
			}
		}
		
		/// <summary>
		/// Overridden BaseVersionDictionary getEnumerator method.
		/// </summary>
		/// <returns></returns>
		protected override IEnumerator getEnumerator()
		{
			System.Collections.IEnumerable retVal;
			sourceLock.AcquireReaderLock(TIMEOUT);
			try
			{
				retVal = (IEnumerable) m_Source.GetEnumerator();
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				sourceLock.ReleaseReaderLock();
			}
			return (IEnumerator) retVal;
		}

		#endregion
		#region IDictionary Members

		/// <summary>
		/// Overridden IDictionary GetEnumerator method.
		/// </summary>
		/// <returns></returns>
		public override IDictionaryEnumerator GetEnumerator()
		{
			IDictionaryEnumerator retVal;
			sourceLock.AcquireReaderLock(TIMEOUT);
			try
			{
				retVal=m_Source.GetEnumerator();
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				sourceLock.ReleaseReaderLock();
			}
			return retVal;
		}

		/// <summary>
		/// Overridden IDictionary Remove method.
		/// </summary>
		/// <param name="key"></param>
		public override void Remove(object key)
		{
			sourceLock.AcquireWriterLock(TIMEOUT);
			cacheLock.AcquireWriterLock(TIMEOUT);
			try
			{
				m_Source.Remove((IComparable) key);
				/** If removing an item, we need to clear the cache because we are not sure how many references to this
				 ** item exist under different keys
				 **/
				m_Cache.Clear();
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				cacheLock.ReleaseWriterLock();
				sourceLock.ReleaseWriterLock();
			}
		}

		/// <summary>
		/// Overridden IDictionary Contains method.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public override bool Contains(object key)
		{
			bool retVal;
			sourceLock.AcquireReaderLock(TIMEOUT);
			cacheLock.AcquireReaderLock(TIMEOUT);
			try
			{
				//We must check both the cache and the source to see if the requested value exists.
				retVal = m_Source.Contains((IComparable) key) || m_Cache.Contains((IComparable) key);
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				cacheLock.ReleaseReaderLock();
				sourceLock.ReleaseReaderLock();
			}
			return retVal;
		}

		
		/// <summary>
		/// Overridden IDictionary Clear method.
		/// </summary>
		public override void Clear()
		{
			sourceLock.AcquireWriterLock(TIMEOUT);
			cacheLock.AcquireWriterLock(TIMEOUT);
			try
			{
				m_Source.Clear();
				m_Cache.Clear();
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				cacheLock.ReleaseWriterLock();
				sourceLock.ReleaseWriterLock();
			}
		}

		/// <summary>
		/// Overridden IDictionary Values property.
		/// </summary>
		public override ICollection Values
		{
			get
			{
				ICollection retVal;
				sourceLock.AcquireReaderLock(TIMEOUT);
				try
				{
					retVal = m_Source.Values;
				}
				catch(Exception e)
				{
					throw;
				}
				finally
				{
					sourceLock.ReleaseReaderLock();
				}
				return retVal;
			}
		}

		
		/// <summary>
		/// Overridden IDictionary Add method.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public override void Add(object key, object value)
		{
			sourceLock.AcquireWriterLock(TIMEOUT);
			try
			{
				if (m_expiretype == null || !(m_Source is INameAddressSupport))
				{
					m_Source.Add((IComparable) key, value);
				}
				else
				{
					((INameAddressSupport) m_Source).put((string) key,value,m_expiretype);
				}
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				sourceLock.ReleaseWriterLock();
			}
		}

		
		/// <summary>
		/// Overridden IDictionary Keys method.
		/// </summary>
		public override ICollection Keys
		{
			get
			{
				ICollection retVal;
				sourceLock.AcquireReaderLock(TIMEOUT);
				try
				{
					retVal = m_Source.Keys;
				}
				catch(Exception e)
				{
					throw;
				}
				finally
				{
					sourceLock.ReleaseReaderLock();
				}
				return retVal;
			}
		}
		
		#endregion

		#region ICollection Members

		/// <summary>
		/// Overridden ICollection Count method.
		/// </summary>
		public override int Count
		{
			get
			{
				int retVal;
				sourceLock.AcquireReaderLock(TIMEOUT);
				try
				{
				retVal = m_Source.Count;
				}
				catch(Exception e)
				{
					throw;
				}
				finally
				{
					sourceLock.ReleaseReaderLock();
				}
				return retVal;
			}
		}

		/// <summary>
		/// Overridden ICollection CopyTo method.
		/// </summary>
		/// <param name="array"></param>
		/// <param name="index"></param>
		public override void CopyTo(Array array, int index)
		{
			sourceLock.AcquireReaderLock(TIMEOUT);
			try
			{
				m_Source.CopyTo(array, index);
			}
			catch(Exception e)
			{
				throw;
			}
			finally
			{
				sourceLock.ReleaseReaderLock();
			}
		}



		#endregion

		#region INameAddressSupport Members

		public bool containsField(string nameAddress)
		{
			return Contains(nameAddress);
		}

		public bool isGetOnly
		{
			get
			{
				return false;
			}
		}

		public Swaf.Container.XmlSerializationSupport SerializationSupport
		{
			get
			{
				return Swaf.Container.XmlSerializationSupport.None;
			}
		}

		public void put(string nameAddress, object val, object extraInfo)
		{
			writeToSource(nameAddress, val, extraInfo);
		}

		void Swaf.Container.INameAddressSupport.put(string name, object newValue)
		{
			writeToSource(name, newValue, null);
		}

		public string getXml(string options)
		{
			throw new NotSupportedException("Xml Serialization is not supported for the HashVersioningDictionary.");
		}

		public object get(string name, params object[] p)
		{
			return getValue(name);
		}

		object Swaf.Container.INameAddressSupport.get(string name, object oneArg)
		{
			return getValue(name);
		}

		object Swaf.Container.INameAddressSupport.get(string name)
		{
			return getValue(name);
		}

		public string Xml
		{
			get
			{
				return getXml("");
			}
		}

		#endregion
	}
}
